﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Multiprocessing.Core.Units;

namespace Multiprocessing.Core.Algorithm
{
    public abstract class BaseAlgorithm<T> : IAlgorithm<T>
    {
        #region Fields

        private readonly IProcessingBlockFactory _processingBlockFactory;
        private readonly Stopwatch _stopwatch;
        private readonly string _name;

        #endregion

        #region Properties

        public string Name
        {
            get { return _name; }
        }

        protected IProcessingBlockFactory Factory
        {
            get { return _processingBlockFactory; }
        }

        protected Stopwatch Stopwatch
        {
            get { return _stopwatch; }
        }

        #endregion

        #region Constructors

        protected BaseAlgorithm(IProcessingBlockFactory processingBlockFactory, string name)
        {
            ArgumentValidator.NullValidate(processingBlockFactory, "processingUnitFactory");
            _processingBlockFactory = processingBlockFactory;
            ArgumentValidator.NullValidate(name, "name");
            _name = name;
            _stopwatch = new Stopwatch();
        }

        protected BaseAlgorithm(string name)
            : this(ProcessingBlockFactory.Instance, name)
        {
        }

        #endregion

        #region Methods

        public AlgorithmResult<T> Execute<TProcessingBlock>(params object[] args)
            where TProcessingBlock : IProcessingBlock
        {
            return this.Execute<TProcessingBlock>(1, args);
        }

        public AlgorithmResult<T> Execute<TProcessingBlock>(int processingUnitCount, params object[] args) 
            where TProcessingBlock : IProcessingBlock
        {
            return this.Execute(_processingBlockFactory.CreateProcessingBlock<TProcessingBlock>(processingUnitCount),
                                args);
        }

        public abstract AlgorithmResult<T> Execute(IProcessingBlock processingBlock, params object[] args);

        AlgorithmResult IAlgorithm.Execute<TProcessingBlock>(params object[] args)
        {
            var result = this.Execute<TProcessingBlock>(args);
            return new AlgorithmResult(result.Result, result.ElapsedTime, result.ElapsedMemory);
        }

        AlgorithmResult IAlgorithm.Execute<TProcessingBlock>(int processingUnitCount, params object[] args)
        {
            var result = this.Execute<TProcessingBlock>(processingUnitCount, args);
            return new AlgorithmResult(result.Result, result.ElapsedTime, result.ElapsedMemory);
        }

        public abstract object[] CreateParameters(int size);

        AlgorithmResult IAlgorithm.Execute(IProcessingBlock processingBlock, params object[] args)
        {
            var result = this.Execute(processingBlock, args);
            return new AlgorithmResult(result.Result, result.ElapsedTime, result.ElapsedMemory);
        }

        #endregion
    }
}
